#pragma once

#include "TcpServer.hpp"

using namespace std;



class EpollServer 
{
public:
    static int gsize;
    using fcn_t = function<int(int)>;
    EpollServer(uint16_t port, fcn_t fcn)
        : mport(port)
        , mepfd(-1)
        , mlisSock(-1)
        , mioFcntor(fcn)
    {}

    // 初始化
    void init()
    {
        mlisSock = Sock::GetSock();
        Sock::Bind(mlisSock, mport);
        Sock::Listen(mlisSock);
        cout << "监听成功" << endl;

        mepfd = epoll_create(gsize);
        if (mepfd < 0)
            cerr << "创建失败" << endl;
        cout << "创建 epoll 模型成功" << endl;
    }

    // 开始运行
    void run()
    {
        // 1. 先添加 lisSock 到监听范围中, 算是起点
        //    往 epoll 模型中加入关心的文件描述符以及对应的事件
        struct epoll_event event;  // event 两个成员 ->  data && events
        event.data.fd = mlisSock;
        event.events = EPOLLIN;   // 关心读取事件

        epoll_ctl(mepfd, EPOLL_CTL_ADD, mlisSock, &event); // 增加监听套接字以及其所关心的事件
        
        // 2. 就绪事件的缓冲区, 用来给 epoll_wait 所使用
        int readySize = 128;
        struct epoll_event ready[readySize];  // 只要放在里面的, 就都是合法的
        int timeout = 1000; // 1000 毫秒
        while (true)
        {
            // 唤醒就绪队列中的一个事件 返回就绪的 fd 的个数
            // 只需要将底层的就绪队列中的结点，依次从 0 下标放到 ready 中即可
            // 所以只需要遍历到 readyn 就可以了, 里面依次就是就绪的事件了
            int readyn = epoll_wait(mepfd, ready, readySize, timeout);
            switch(readyn)
            {
            case 0:
                cout << "超时" << (unsigned long) time(nullptr) << endl;
                break;
            case -1:
                cerr << "错误" << endl;
                break;
            default:
                cout << "事件 [个数: " << readyn << "] 就绪，等待处理：" << endl;
                handleEvents(ready, readyn);
                break;
            }
        }
    }

    void handleEvents(struct epoll_event* ready, int readyn)
    {
        for (int i = 0; i < readyn; i ++)
        {
            int sock = ready[i].data.fd;
            // 通过 epoll_wait 提取出来的，就是内核告知用户的, 所以也就是所谓 revents
            uint32_t revents = ready[i].events; 
            // 开始检测事件
            // 读事件就绪, 有两种可能, 1. 普通套接字的连接   2. 监听套接字的连接
            if (revents & EPOLLIN)                                      // 有读事件
            {
                if (sock == mlisSock)                                   // 监听套接字, 可以获取新连接
                {
                    string cltIp;
                    uint16_t cltPort = 0;
                    int svr = Sock::Accept(mlisSock, &cltIp, &cltPort);
                    if (svr >= 0)
                        cout << "获取新连接成功" << endl;
                    
                    // 新连接交给 epoll
                    struct epoll_event connect;
                    connect.data.fd = svr;                              // 建立文件描述符
                    connect.events |= EPOLLIN;
                    epoll_ctl(mepfd, EPOLL_CTL_ADD, svr, &connect);
                }
                else                                                    // 普通套接字, 进行数据 IO
                {
                    // 进行 IO 操作, 根据传入的函数
                    int ret = mioFcntor(sock);
                    if (ret <= 0)                                       // 如果读取结束, 也就说明本次连接完成
                    {
                        close(sock);
                        epoll_ctl(mepfd, EPOLL_CTL_DEL, sock, nullptr); // 删除的话, 不关心 event, 传入 sock 即可
                        cout << "客户端[" << sock << "] 本次连接完成" << endl;
                        close(sock);                                    // 先从红黑树中移除, 再关闭这个文件
                    }
                }
            }
            else 
            {

            }
        }
    }

    ~EpollServer()
    {
        if (mlisSock != -1)
            close(mlisSock);
        if (mepfd != -1)
            close(mepfd);
    }
private:
    uint16_t mport;
    int mepfd; // epoll 模型
    int mlisSock;
    fcn_t mioFcntor;
};

int EpollServer::gsize = 1024;